/*******************************************
 * Copyright 2007 Aphrodite Framework for javaME .
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * Email : owner@aphroditeme.dev.java.net
 * Contributors:
 *     Kent Elton - initial API and implementation
 *******************************************/

package com.aphrodite.util;

import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

import net.rim.device.api.smartcard.AnswerToReset;

/**
 * Date/Time Conversion and manipulation functions
 * 
 * @author kentelt
 * 
 */
public class DateTimeUtil {

	private static final String[] MONTH_LABELS = new String[] { "January", "February", "March", "April", "May", "June",
			"July", "August", "September", "October", "November", "December" };

	/**
	 * @see DateTimeUtil#formatDate(Date, int)
	 */
	public static final int TYPE_DATE = 1;

	/**
	 * @see DateTimeUtil#formatDate(Date, int)
	 */
	public static final int TYPE_DATE_TIME = 3;

	/**
	 * @see DateTimeUtil#formatDate(Date, int)
	 */
	public static final int TYPE_TIME = 2;

	private static final String[] WEEKDAY_LABELS = new String[] { "M", "T", "W", "T", "F", "S", "S" };

	private static String addInitialZero(String s) {
		if (s.length() == 1) {
			s = ("0" + s);
		}

		return s;
	}
	
	/**
	 * Checks if the given time (in milliseconds) is the current day
	 * @param time
	 * @return
	 */
//	public static boolean isToday(long time){
//		Calendar current=Calendar.getInstance();
//		current.setTime(new Date(System.currentTimeMillis()));
//		
//		
//	}
	
	/**
	 * Check if the current time is AM or PM
	 * @param time
	 * @return true is the given <code>time</code> is AM .
	 */
	public static boolean isAM(long time){
		Calendar cal=Calendar.getInstance();
		cal.setTime(new Date(time));
		int am_pm=cal.get(Calendar.AM_PM);
		return am_pm==Calendar.AM;
		
	}

	private static void dd(StringBuffer buf, int i) {
		buf.append((char) (('0') + i / 10));
		buf.append((char) (('0') + i % 10));
	}

	/**
	 * Formats the given date to ISO format ie. YYYY-MM-DD
	 * 
	 * @param date
	 * @param type
	 * @return String representing the formatted date.
	 */
	public static String formatDate(Date date, int type) {

		Calendar c = Calendar.getInstance();
//		c.setTimeZone(TimeZone.getTimeZone("GMT"));
		c.setTime(date);

		StringBuffer buf = new StringBuffer();

		if ((type & TYPE_DATE) != 0) {
			int year = c.get(Calendar.YEAR);
			dd(buf, year / 100);
			dd(buf, year % 100);
			buf.append('-');
			dd(buf, c.get(Calendar.MONTH) - Calendar.JANUARY + 1);
			buf.append('-');
			dd(buf, c.get(Calendar.DAY_OF_MONTH));
		}

		if ((type & TYPE_TIME) != 0) {
			buf.append(' ');
			buf.append(formatTime(c));
		}

		return buf.toString();
	}

	private static String formatTime(Calendar cal) {
		String hour = Integer.toString(cal.get(Calendar.HOUR_OF_DAY));
		String min = Integer.toString(cal.get(Calendar.MINUTE));
		String sec = Integer.toString(cal.get(Calendar.SECOND));
		String millisec = Integer.toString(cal.get(Calendar.MILLISECOND));

		if (hour.length() == 1)
			hour = "0" + hour;
		if (min.length() == 1)
			min = "0" + min;
		if (sec.length() == 1)
			sec = "0" + sec;
		if (millisec.length() == 1)
			millisec = "00" + millisec;
		else if (millisec.length() == 2)
			millisec = "0" + millisec;

		StringBuffer buf = new StringBuffer();
		buf.append(hour).append(':').append(min).append(':').append(sec).append('.').append(millisec);
		return buf.toString();

	}

	/** Get a "HH:MM:SS.MS representation of a Date */
	public static String formatTime(Date date) {
		final Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		return formatTime(cal);
	}

	/**
	 * Get the number of days in the given month
	 * 
	 * @param calendar
	 *            instance
	 * @return Days in given month
	 */
	public static int getDaysInMonth(Calendar calendar) {
		return getDaysInMonth(calendar.get(Calendar.MONTH), calendar.get(Calendar.YEAR));
	}

	/**
	 * Get the days in the given month
	 * 
	 * @param month
	 * @param year
	 * @return Days in the given month
	 */
	public static int getDaysInMonth(int month, int year) {
		switch (month) {
		case 3:
		case 5:
		case 8:
		case 10:
			return 30;
		case 1:
			return year % 4 == 0 && year % 100 != 0 ? 29 : 28;
		default:
			return 31;
		}
	}

	/**
	 * @param cal
	 * @return
	 */
	public static String getMonth(Calendar cal) {
		return getMonth(cal.get(Calendar.MONTH));
		// if (cal.get(Calendar.MONTH) == Calendar.JANUARY) {
		// ret = "Jan.";
		// } else if (cal.get(Calendar.MONTH) == Calendar.FEBRUARY) {
		// ret = "Feb.";
		// } else if (cal.get(Calendar.MONTH) == Calendar.MARCH) {
		// ret = "Mar.";
		// } else if (cal.get(Calendar.MONTH) == Calendar.APRIL) {
		// ret = "Apr.";
		// } else if (cal.get(Calendar.MONTH) == Calendar.MAY) {
		// ret = "May";
		// } else if (cal.get(Calendar.MONTH) == Calendar.JUNE) {
		// ret = "June";
		// } else if (cal.get(Calendar.MONTH) == Calendar.JULY) {
		// ret = "July";
		// } else if (cal.get(Calendar.MONTH) == Calendar.AUGUST) {
		// ret = "Aug.";
		// } else if (cal.get(Calendar.MONTH) == Calendar.SEPTEMBER) {
		// ret = "Sept.";
		// } else if (cal.get(Calendar.MONTH) == Calendar.OCTOBER) {
		// ret = "Oct.";
		// } else if (cal.get(Calendar.MONTH) == Calendar.NOVEMBER) {
		// ret = "Nov.";
		// } else if (cal.get(Calendar.MONTH) == Calendar.DECEMBER) {
		// ret = "Dec.";
		// }
		//
		// return ret;
	}

	/**
	 * Returns the month for the given index
	 * 
	 * @param index
	 *            Starts from 0 and ends with 11
	 * @return Month representation of the given <code>index</code>
	 */
	public static String getMonth(int index) {
		return MONTH_LABELS[index];
	}

	/**
	 * The default printing pattern is: "DD.MM., hh:mm".
	 * 
	 * @param millis
	 * @return Date in <code>DD.MM., hh:mm</code> format
	 */
	public static String getShortDate(long millis) {
		String ret = "";

		Calendar cal = Calendar.getInstance();
		cal.setTime(new Date(millis));

		Calendar todayCal = Calendar.getInstance();
		todayCal.setTime(new Date(System.currentTimeMillis()));

		if (!sameDay(cal, todayCal)) {
			String month = getMonth(cal);
			String day = String.valueOf(cal.get(Calendar.DATE));

			month = addInitialZero(month);
			day = addInitialZero(day);
			ret = day + "." + month + " ";
		}

		return ret;
	}

	/**
	 * 
	 * @param startDate
	 *            Interval start date time
	 * @param endDate
	 *            Interval end date time
	 * @return Time interval in format hh:mm:ss
	 */
	public static String getTimeInterval(Date startDate, Date endDate) {

		Calendar cal = Calendar.getInstance();
		cal.setTime(startDate);
		// System.out.println("Start: " + cal.get(Calendar.HOUR_OF_DAY));
		cal.setTime(endDate);
		// System.out.println("End: " + cal.get(Calendar.HOUR_OF_DAY));

		long intervalSeconds = (endDate.getTime() - startDate.getTime()) / 1000L;
		return printTime(intervalSeconds, true, ':');
		// long hours = intervalSeconds / 3600L;
		// long minutes = (intervalSeconds % 3600L) / 60L;
		// long seconds = (intervalSeconds % 3600L) % 60L;
		// long days = hours / 24L;
	}

	/**
	 * 
	 * @param index
	 *            From 0 to 6
	 * @return String representing the day
	 */
	public static String getWeekday(int index) {
		return WEEKDAY_LABELS[index];
	}

	/**
	 * Parses a String for a "EEE, dd MMM yyyy hh:mm:ss 'GMT'" formatted Date
	 */
	public static Date parseEEE_DD_MM_YYYY(String str, int index) {
		final String[] MONTHS = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
		Calendar cal = Calendar.getInstance();
		char[] delim = { ' ', ',', ':' };
		StringParser par = new StringParser(str, index);
		String EEE = par.getString(); // day of the week
		int day = par.getInt(); // day of the month
		String MMM = par.getString(); // month
		int month = 0;
		for (; month < 12; month++)
			if (MMM.equalsIgnoreCase(MONTHS[month]))
				break;
		if (month == 12)
			return null; // ERROR..
		// else
		int year = par.getInt();
		int hour = Integer.parseInt(par.getWord(delim));
		int min = Integer.parseInt(par.getWord(delim));
		int sec = Integer.parseInt(par.getWord(delim));

		cal.set(Calendar.YEAR, year);
		cal.set(Calendar.MONTH, month);
		cal.set(Calendar.DAY_OF_MONTH, day);
		cal.set(Calendar.HOUR_OF_DAY, hour);
		cal.set(Calendar.MINUTE, min);
		cal.set(Calendar.SECOND, sec);

		return cal.getTime();
	}

	/**
	 * returns the time in HH:MM:SS
	 * 
	 * @param millis
	 *            time in milliseconds
	 * @param showSeconds
	 *            show seconds measure
	 * @param separator
	 *            separator to be used.
	 * @return time in HH{separator}MM{separator}SS
	 */
	public static String printTime(long millis, boolean showSeconds, char separator) {
		String ret;
		String hour = "-";
		String min = "-";
		String sec = "-";

		if (millis > 0) {
			Calendar cal = Calendar.getInstance();
			cal.setTime(new Date(millis));

			hour = String.valueOf(cal.get(Calendar.HOUR_OF_DAY));
			min = String.valueOf(cal.get(Calendar.MINUTE));
			sec = String.valueOf(cal.get(Calendar.SECOND));

			hour = addInitialZero(hour);
			min = addInitialZero(min);
			sec = addInitialZero(sec);
		}

		ret = hour + separator + min;

		if (showSeconds) {
			ret = ret + separator + sec;
		}

		return ret;
	}

	private static boolean sameDay(Calendar cal, Calendar todayCal) {
		return (((cal.get(Calendar.DAY_OF_MONTH) == todayCal.get(Calendar.DAY_OF_MONTH)) && (cal.get(Calendar.MONTH) == todayCal
				.get(Calendar.MONTH))) && (cal.get(Calendar.YEAR) == todayCal.get(Calendar.YEAR)));
	}

	/**
	 * Converts the given string representation of a date into a date Object.
	 * Ihe input format is : yyyy-mm-dd. If null is passed a Date object with
	 * date set to 1-1-1970 is returned
	 * 
	 * @param value
	 *            The string representation of a date.
	 * @return The date object.
	 */
	public static Date toDate(String value) {
		Date date;
		if (value == null)
			date = null;
		else {
			int[] fields = { 1970, // year
					1, // month
					1, // day of month
			};
			int fieldIdx = 0;
			StringBuffer sbField = new StringBuffer();
			for (int i = 0, count = value.length(); i < count; i++) {
				char ch = value.charAt(i);
				if (ch != '-')
					sbField.append(ch);
				if (ch == '-' || i == count - 1) {
					String strField = sbField.toString();
					sbField.setLength(0);
					try {
						fields[fieldIdx] = Integer.parseInt(strField);
					} catch (Exception exc) {
					}
					if (++fieldIdx >= fields.length)
						break;
				}
			}
			Calendar c = Calendar.getInstance();
			c.set(Calendar.YEAR, fields[0]);
			c.set(Calendar.MONTH, fields[1] - 1);
			c.set(Calendar.DAY_OF_MONTH, fields[2]);
			date = c.getTime();
		}
		return date;
	}

	// // Creates array of calendar values form value of seconds since 1st jan
	// 1970
	// // (GMT)
	// public static int[] createDate(long value) {
	// int total_days, last_days, i;
	// int sec, min, hour, day, mon, year;
	//
	// sec = (int) (value % 60);
	//
	// min = (int) ((value / 60) % 60); // min
	// value -= 60 * min;
	//
	// hour = (int) ((value / 3600) % 24); // hour
	// value -= 3600 * hour;
	//
	// total_days = (int) (value / (3600 * 24));
	//
	// year = 1970;
	// for (;;) {
	// last_days = total_days - ((year % 4 == 0) && (year != 2000) ? 366 : 365);
	// if (last_days <= 0)
	// break;
	// total_days = last_days;
	// year++;
	// } // year
	//
	// int febrDays = ((year % 4 == 0) && (year != 2000)) ? 29 : 28;
	//
	// mon = 1;
	// for (i = 0; i < 12; i++) {
	// last_days = total_days - ((i == 1) ? febrDays : dayCounts[i]);
	// if (last_days <= 0)
	// break;
	// mon++;
	// total_days = last_days;
	// } // mon
	//
	// day = total_days; // day
	//
	// return new int[] { sec, min, hour, day, mon, year };
	// }

	private DateTimeUtil() {}
}
